//------------------------------------------------------------
//  Filename: eth_mac_addswap.sv
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2020-12-10 17:08
//  Description: 
//   
//  Copyright (C) 2014, YRBD, Inc. 					      
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_addswap #( 
    parameter DEEPTH = 8,  // mus bigger than 6 
    parameter PTR_WIDTH = $clog2(DEEPTH)
) 
( 
    input  logic            clk_i, 
    input  logic            rstn_i, 

    input  logic            mac_cfg_loop , 
    input  logic            mac_cfg_byps , 
    input  logic[47:0]      mac_uni_addr , 

    input  logic[7:0]       mac_ft_data  ,   
    input  logic            mac_ft_valid , 
    input  logic            mac_ft_sof   ,   
    input  logic            mac_ft_eof   ,   
    output logic            mac_lp_ready ,  

    input  logic[7:0]       mac_sw_data  ,   
    input  logic            mac_sw_sof   ,   
    input  logic            mac_sw_eof   ,   
    input  logic            mac_sw_valid , 
    output logic            mac_sw_ready , 

    output logic[7:0]       mac_tx_data  ,   
    output logic            mac_tx_eof   ,   
    output logic            mac_tx_valid , 
    input  logic            mac_tx_ready 
);
//----------------------------------------------------------
logic[7:0]            tx_data ;   
logic                 tx_eof  ;   
logic                 tx_sof  ;   
logic                 tx_valid; 
logic                 lp_valid; 
logic[7:0]            lp_data ;   
logic[3:0]            tx_cnt  ;   
logic[3:0]            swp_cnt ;   
logic[7:0]            swp_byte;
//----------------------------------------------------------
logic                 rstn;
logic [7 : 0]         wdata; // 
logic                 wvalid; 
logic                 wready;
logic [7 : 0]         rdata; // 
logic                 rvalid; 
logic                 rready;
//----------------------------------------------------------
logic[PTR_WIDTH-1: 0] buf_wr; 
logic[PTR_WIDTH-1: 0] buf_rd; 
logic[PTR_WIDTH-1: 0] buf_wr_q; 
logic[PTR_WIDTH-1: 0] buf_rd_q; 
logic[PTR_WIDTH  : 0] buf_cnt; 
logic[PTR_WIDTH  : 0] buf_cnt_q;
//----------------------------------------------------------
assign rstn = (~mac_cfg_byps)&rstn_i;
//----------------------------------------------------------
assign tx_data  = (mac_cfg_loop)?(mac_ft_data ):(mac_sw_data );   
assign tx_eof   = (mac_cfg_loop)?(mac_ft_eof  ):(mac_sw_eof  );   
assign tx_sof   = (mac_cfg_loop)?(mac_ft_sof  ):(mac_sw_sof  );   
assign tx_valid = (mac_cfg_loop)?(mac_ft_valid):(mac_sw_valid); 
//----------------------------------------------------------
always_ff@(posedge clk_i,negedge rstn)begin 
    if(!rstn)begin 
        buf_wr_q <= 'h0; 
    end
    else if(wvalid&wready) begin 
        buf_wr_q <= buf_wr; 
    end 
end 
//----------------------------------------------------------
assign buf_wr = (wvalid&wready)?(buf_wr_q + 1): buf_wr_q; 
//----------------------------------------------------------
always_ff@(posedge clk_i,negedge rstn)begin 
    if(!rstn)begin 
        buf_rd_q <= 'h0; 
    end
    else if(rvalid&rready) begin 
        buf_rd_q <= buf_rd; 
    end 
end 
//----------------------------------------------------------
assign buf_rd = (rvalid&rready)?(buf_rd_q + 1):buf_rd_q; 
//----------------------------------------------------------
enum logic[4:0] {IDLE,SWAP0,HEAD,SWAP1,SWAP2,KEEP} cs,ns;
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn) begin
    if(~rstn) begin
        cs <= IDLE;
    end
    else begin
        cs <= ns;
    end
end
//--------------------------------------------------------
always_comb begin
    ns = cs;
    case(cs)
        IDLE: begin
            if(tx_sof) ns = SWAP0;
        end
        SWAP0: begin
            if(tx_cnt > 3) ns = HEAD;
        end 
        HEAD: begin
            if(tx_cnt > 5) ns = SWAP1;
        end
        SWAP1: begin
            if(tx_cnt > 5) ns = SWAP2;
        end
        SWAP2: begin
            if(tx_cnt == 0) ns = KEEP;
        end
        KEEP: begin
           if(buf_cnt_q == 0) ns = IDLE;
        end
    endcase
end
//----------------------------------------------------------
always_comb begin
    rvalid = 1'b0;
    if((cs == HEAD)||(cs == SWAP1)||(cs == KEEP))begin
        rvalid = (buf_cnt_q > 0);
    end
end
//----------------------------------------------------------
assign lp_valid = (cs == SWAP2) ? 1'b1 : rvalid ;
assign lp_data  = (cs == SWAP2) ? swp_byte : rdata ;
//----------------------------------------------------------
always_comb begin
    if ((cs == IDLE)||(cs == SWAP0)) begin
        wready = 'b0;
    end
    else begin
        wready = (buf_cnt_q < DEEPTH);
    end
end
//----------------------------------------------------------
always_comb begin
    if ((cs == IDLE)||(cs == SWAP0)) begin
        mac_lp_ready = 'b1;
    end
    else begin
        mac_lp_ready = (buf_cnt_q < DEEPTH);
    end
end
//----------------------------------------------------------
always_comb begin 
    buf_cnt = buf_cnt_q; 
    if(wvalid&wready&rvalid&rready)begin 
        buf_cnt = buf_cnt_q; 
    end 
    else if(rvalid&rready) begin 
        buf_cnt = buf_cnt_q - 1'b1;
    end 
    else if(wvalid&wready) begin 
        buf_cnt = buf_cnt_q + 1'b1; 
    end 
end 
//----------------------------------------------------------
always_ff@(posedge clk_i,negedge rstn)begin 
    if(!rstn)begin 
        buf_cnt_q <= 'h0; 
    end 
    else begin 
        buf_cnt_q <= buf_cnt; 
    end 
end 
//----------------------------------------------------------
always_comb begin
    swp_byte = 0;
    if(swp_cnt < 6) swp_byte = mac_uni_addr[swp_cnt*8 +: 8]; 
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn) begin
    if(~rstn) begin
        swp_cnt <= 'b0;
    end
    else if(cs == HEAD) begin
        swp_cnt <= 'b0;
    end
    else if((cs == SWAP1)||(cs == SWAP2)) begin
        swp_cnt <= tx_cnt - 1'b1;
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn) begin
    if(~rstn) begin
        tx_cnt <= 'b0;
    end
    else if( (cs == IDLE )  ||
            ((cs == SWAP0)&&(ns == HEAD )) ||
            ((cs == HEAD )&&(ns == SWAP1))) begin
        tx_cnt <= 'b0;
    end
    else if(cs == SWAP1) begin
        if(mac_tx_ready) tx_cnt <= tx_cnt + 1'b1;
    end
    else if(cs == SWAP2) begin
        if(mac_tx_ready) tx_cnt <= tx_cnt - 1'b1;
    end
    else if(tx_valid) begin
        tx_cnt <= tx_cnt + 1'b1;
    end
end
//----------------------------------------------------------
logic[7:0] buf_bits[DEEPTH-1:0]; 
//----------------------------------------------------------
genvar i;
generate 
    for (i = 0;i < DEEPTH; i ++) begin
        always_ff@(posedge clk_i,negedge rstn)begin
            if(!rstn)begin 
                buf_bits[i] <= '0; 
            end 
            else if(wvalid&wready&(buf_wr_q == i)) begin
                buf_bits[i] <= wdata; 
            end 
        end 
    end
endgenerate
//----------------------------------------------------------
logic[7:0] rdata_q;
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn) begin
    if(~rstn) begin
        rdata_q <= 'b0;
    end
    else begin
        rdata_q <= rdata;
    end
end
//----------------------------------------------------------
assign rdata  = (rvalid&rready)? buf_bits[buf_rd_q]:rdata_q; 
assign rready = mac_tx_ready;
//----------------------------------------------------------
assign wdata  = tx_data;
assign wvalid = tx_valid;
//----------------------------------------------------------
assign mac_tx_data  = mac_cfg_byps ? tx_data : lp_data;
assign mac_tx_valid = mac_cfg_byps ? tx_valid: lp_valid;
assign mac_tx_eof   = mac_cfg_byps ? tx_eof  : (cs == KEEP)&(buf_cnt_q == 1);
//----------------------------------------------------------
assign mac_sw_ready = mac_cfg_byps ? mac_tx_ready : wready;

endmodule
